using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Db4objects.Db4o.Linq;
using SharedObjects;

namespace TheGameDatabaseTool
{
	public partial class DatabaseMainScript
	{
		private Program CreateProgram(ulong id, ulong interfaceId, string name, ProgramCategory category, bool allowMod, bool adminOnly, bool isDefault, int size, double speedMod, SpeedType speedType, int version, int basePrice, string method)
		{
			Program prog = GetEntity<Program>(id);
			prog.AllowModification = allowMod;
			prog.Category = category;
			prog.InterfaceId = interfaceId;
			prog.IsAdminOnly = adminOnly;
			prog.IsDefault = isDefault;
			prog.IsDeleted = false;
			prog.MethodName = method;
			prog.Name = name;
			prog.Size = size;
			prog.SpeedModifier = speedMod;
			prog.SpeedType = speedType;
			prog.Version = version;
			prog.BasePrice = basePrice;
			return prog;
		}

		private Bypasser CreateBypasser(ulong id, ulong interfaceId, string name, bool allowMod, ulong bypassableSecSysId, ProgramCategory programCategory, bool adminOnly, bool isDefault, string method, int size, int version, double speedMod, SpeedType speedType, int basePrice)
		{
			Bypasser b = GetEntity<Bypasser>(id);
			b.InterfaceId = interfaceId;
			b.AllowModification = allowMod;
			b.BypassableSecuritySystem = bypassableSecSysId;
			b.Category = programCategory;
			b.IsAdminOnly = adminOnly;
			b.IsDefault = isDefault;
			b.MethodName = method;
			b.Name = name;
			b.Size = size;
			b.SpeedModifier = speedMod;
			b.SpeedType = speedType;
			b.Version = version;
			b.BasePrice = basePrice;
			return b;
		}

		private SecuritySystem CreateSecuritySystem(ulong id, string name, CredentialType credType, int difficulty, int basebypasstime, string methodname, int version, bool active)
		{
			SecuritySystem secSys = GetEntity<SecuritySystem>(id);
			secSys.Name = name;
			secSys.CredentialType = credType;
			secSys.MethodName = methodname;
			secSys.DifficultyIdentifier = difficulty;
			secSys.BaseBypassTime = basebypasstime;
			secSys.Version = version;
			secSys.IsActive = active;
			return secSys;
		}

		private ComputerSystemFeature CreateSystemFeature(ulong id, string name, bool hackedOnly, bool active)
		{
			ComputerSystemFeature csf = GetEntity<ComputerSystemFeature>(id);
			csf.Name = name;
			csf.IsActive = active;
			csf.HackedOnly = hackedOnly;
			return csf;
		}

// ReSharper disable UnusedMember.Local
		private string CreateFilename(string username, DateTime date)
		{
			return string.Format("{0}.{1}", username.Replace(" ", "."), date.ToBinary());
		}
// ReSharper restore UnusedMember.Local

		private void InitializeNameLists()
		{
			_lastNames = GetNamesFromFile("..\\..\\Surnames.txt");
			_femFirstnames = GetNamesFromFile("..\\..\\FemaleFirstnames.txt");
			_maleFirstnames = GetNamesFromFile("..\\..\\MaleFirstnames.txt");
			_femFirstnames = _femFirstnames.Except(_maleFirstnames).ToList();
			_maleFirstnames = _maleFirstnames.Except(_femFirstnames).ToList();
			List<string> corpNames = GetNamesFromFile("..\\..\\CorpNamesGuids.txt").Distinct().OrderBy(it => it).ToList();
			_corpNamesWithGuid = new Dictionary<string, Guid>();
			_corporationIdToComputersystemId = new Dictionary<ulong, List<Guid>>();
			foreach (string s in corpNames)
			{
				var values = s.Split(new[] {','});
				string name = values[0];
				Guid id = new Guid(values[1]);
				if(_corpNamesWithGuid.ContainsKey(name))
					continue;
				
				_corpNamesWithGuid.Add(name, id);
				List<string> guidStrings = new List<string>(values);				
				guidStrings.RemoveRange(0, 2);
				List<Guid> guids = guidStrings.Select(it => new Guid(it)).ToList();
				_corporationIdToComputersystemId.Add(id.ToLong(), guids);
			}
		}

		private List<string> GetNamesFromFile(string path)
		{
			List<string> total = File.ReadAllLines(path, Encoding.UTF8)
				.Where(it => !it.Contains(":") && !string.IsNullOrEmpty(it) && !string.IsNullOrWhiteSpace(it))
				.ToList();

			for (int i = 0; i < total.Count; i++)
				total[i] = total[i].Trim().UppercaseFirst();

			return total;
		}

		private string GenerateRandomPassword(int length)
		{
			if(length <= 8)
				throw new ArgumentException("length must be greater 8.", "length");
#warning ##### Replace with true randomization!
			return "test";
		}

		private string GenerateRandomName()
		{
			var person = GenerateRandomPerson();
			return string.Format("{0} {1}", person.Firstname, person.Lastname);
		}

		private Person GenerateRandomPerson()
		{
			if (_seeder == null)
				_seeder = new Random();
			Random rand = new Random(Environment.TickCount);
			while (true)
			{
				Gender gender = rand.Next(0, 99) < 50 ? Gender.Male : Gender.Female;
				string firstname = gender == Gender.Male ? _maleFirstnames[rand.Next(0, _maleFirstnames.Count - 1)] : _femFirstnames[rand.Next(0, _femFirstnames.Count - 1)];
				string lastname = _lastNames[rand.Next(0, _lastNames.Count - 1)];

				if (_usedNames.Where(it => it.Firstname == firstname && it.Lastname == lastname && it.Gender == gender).Count() > 0)
					continue;

				Person p = new Person { Firstname = firstname, Lastname = lastname, Gender = gender };
				_usedNames.Add(p);
				return p;
			}
		}

		private NetworkAddress CreateNetworkAddress(ulong id, byte classA, byte classB, byte classC, byte classD)
		{
			NetworkAddress addr = GetEntity<NetworkAddress>(id);
			addr.ClassA = addr.ClassA != 0 ? addr.ClassA : classA;
			addr.ClassB = addr.ClassB != 0 ? addr.ClassB : classB;
			addr.ClassC = addr.ClassC != 0 ? addr.ClassC : classC;
			addr.ClassD = addr.ClassD != 0 ? addr.ClassD : classD;
			return addr;
		}

		private T GetEntity<T>(ulong id) where T : EntityBase, new()
		{
			T ret = (from T x in Db where x.Id == id select x).FirstOrDefault() ?? new T { Id = id };
			return ret;
		}

		private BankAccount GetSystemBankAccount(ulong bankAccountSystemId)
		{
			BankAccount ba = (from BankAccount b in Db where b.SystemId == bankAccountSystemId select b).FirstOrDefault() ??
			                 new BankAccount {AccountNumber = Utils.GetBankAccountNumber(Db, bankAccountSystemId), CachedAmount = 0, Id = Guid.NewGuid().ToLong(), IsInvisible = true, IsSystem = true, SystemId = bankAccountSystemId};
			return ba;
		}

		private void DistributeCredentialFiles()
		{
			var accounts = (from ComputerSystemAccount csa in Db select csa);
			Func<ulong, string, bool> dupecheckFileCopy = (sourceId, fname) =>
			{
				if ((from SystemFile f in Db where f.ParentId == _socialSecurityDb.RootDirectory.Id && !f.IsDeleted && (f.CopyOf == sourceId || f.Name.ToLower().StartsWith(fname)) select f).Count()>0)
					return true;
				if ((from SystemFile f in Db where f.ParentId == _passportDb.RootDirectory.Id && !f.IsDeleted && (f.CopyOf == sourceId || f.Name.ToLower().StartsWith(fname)) select f).Count()>0)
					return true;
				if ((from SystemFile f in Db where f.ParentId == _medicalRecordsDb.RootDirectory.Id && !f.IsDeleted && (f.CopyOf == sourceId || f.Name.ToLower().StartsWith(fname)) select f).Count()>0)
					return true;
				if ((from SystemFile f in Db where f.ParentId == _securityClearanceDb.RootDirectory.Id && !f.IsDeleted && (f.CopyOf == sourceId || f.Name.ToLower().StartsWith(fname)) select f).Count()>0)
					return true;
				return false;
			};
			foreach (ComputerSystemAccount i in accounts)
			{
				string username = i.Username.Replace(" ", "");
				foreach (FileCredential c in i.Credentials.OfType<FileCredential>())
				{
					if(dupecheckFileCopy(c.Id, username.ToLower()))
						continue;
					SystemFile f = c.GetCredentialFileCopy(Db);
					f.IsLocked = true;
					if (c is FingerprintCredential)
					{
						f.Name = username + ".FPRINT";
						f.EncryptionLevel = 2;
						f.ParentId = _socialSecurityDb.RootDirectory.Id;
					}
					if (c is RetinalScanCredential)
					{
						f.Name = username + ".RSCAN";
						f.EncryptionLevel = 3;
						f.ParentId = _passportDb.RootDirectory.Id;
					}
					if (c is DnaCredential)
					{
						f.Name = username + ".DNA";
						f.EncryptionLevel = 4;
						f.ParentId = _medicalRecordsDb.RootDirectory.Id;
					}
					if (c is PsecCredential)
					{
						f.Name = username + ".PSEC";
						f.EncryptionLevel = 5;
						f.ParentId = _securityClearanceDb.RootDirectory.Id;
					}
					Db.PersistEntity(f);
				}
			}
		}
	}

	public static class Extensions
	{
		public static string UppercaseFirst(this string s)
		{
			if (string.IsNullOrEmpty(s))
				return null;
			string[] parts = s.Split(new[] { ' ' });
			for (int i = 0; i < parts.Count(); i++)
			{
				char[] a = parts[i].ToLower().ToCharArray();
				a[0] = char.ToUpper(a[0]);
				parts[i] = new string(a);
			}
			return string.Join(" ", parts);
		}

		public static bool Between(this Int32 num, int min, int max)
		{
			if (num >= min && num <= max)
				return true;
			return false;
		}
	}
}